import pandas as pd
import os
from abc import ABC, abstractmethod
import bisect
def extract_symbol_base(symbol_key):
    """
    从品种代码中提取基础品种名称(去掉周期后缀)

    Args:
        symbol_key: 完整品种代码, 如 "CFFEX_IF00_60", "CFFEX_IC00_86400"

    Returns:
        str: 基础品种代码, 如 "CFFEX_IF00", "CFFEX_IC00"

    Examples:
        >>> extract_symbol_base("CFFEX_IF00_60")
        "CFFEX_IF00"
        >>> extract_symbol_base("CFFEX_IC00_86400")
        "CFFEX_IC00"
        >>> extract_symbol_base("INVALID_SYMBOL")
        "INVALID_SYMBOL"
    """
    parts = symbol_key.split("_")
    if len(parts) > 1:
        # 去掉最后一部分(周期)
        return "_".join(parts[:-1])
    else:
        # 如果没有下划线,返回原样
        return symbol_key

def export_results_to_csv(results):
    """用于多核优化的结果导出CSV"""
    # 创建report目录
    os.makedirs("report", exist_ok=True)
    # 准备数据
    data = []
    for result in results:
        strategy_name = result["strategy_name"]
        params_name = result["params_name"]
        metrics = result["results"]
        if metrics is not None:
            data.append({
                "参数组合": params_name,
                "初始资金": metrics[0],
                "最终资产": metrics[1],
                "总盈亏": metrics[2],
                "总收益率(%)": metrics[3],
                "年化收益率(%)": metrics[4],
                "最大回撤(%)": metrics[5],
                "总交易次数": int(metrics[6]),
                "交易天数": int(metrics[7]),
            })
    
    if data:
        df = pd.DataFrame(data)
        # 保存CSV
        csv_path = f"report/{strategy_name}_optimization_result.csv"
        df.to_csv(csv_path, index=False, encoding='utf-8-sig')
    else:
        print("❌ 没有有效的回测结果")
        return None
    


class PythonStrategy:
    """
    策略实现类
    使用向量化指标和二分查找优化回测性能
    """
    def __init__(self, backtest_instance,trade_data,extra_data):
        """
        初始化策略
        
        Args:
            backtest_instance: 回测引擎实例
        """
        self.backtest_instance = backtest_instance
        self.trade_data = trade_data
        self.extra_data = extra_data
        # 初始化所有品种的索引(合并两个合约池)
        self.symbol_index = {}
        self._init_indexes()

    def _init_indexes(self):
        """
        初始化所有品种的查找索引

        为交易数据合约池和跨周期数据合约池中的所有品种
        初始化当前位置索引,用于快速顺序查找
        """
        # 交易数据合约池
        for symbol in self.trade_data.keys():
            self.symbol_index[symbol] = 0

        # 跨周期数据合约池
        for symbol in self.extra_data.keys():
            self.symbol_index[symbol] = 0

    def get_recent_data(self, symbol, timestamp, lookback=11, require_exact_match=True):
        """
        使用顺序查找优化性能

        Args:
            symbol: 品种代码,如 "CFFEX_IF00_60", "CFFEX_IC00_86400"
            timestamp: 当前时间戳,采用基准合约的
            lookback: 需要的数据长度,默认11条
            require_exact_match: 匹配模式
                True - 交易数据:精确匹配当前时间戳
                False - 跨周期数据:避免未来函数

        Returns:
            DataFrame: 最近N条数据,如果不满足条件返回None
        """
        # 确定数据来源
        if symbol in self.trade_data:
            data = self.trade_data[symbol]
        elif symbol in self.extra_data:
            data = self.extra_data[symbol]
        else:
            return None

        ts_list = data["timestamps"]
        if len(ts_list) == 0:
            return None
        # 检查索引是否有效
        if symbol not in self.symbol_index:
            self.symbol_index[symbol] = 0

        current_idx = self.symbol_index[symbol]
        # 快速前进到目标位置(利用K线顺序性)
        while current_idx < len(ts_list) - 1 and ts_list[current_idx] < timestamp:
            current_idx += 1
        # 更新索引
        self.symbol_index[symbol] = current_idx
        if require_exact_match:
            # 交易数据:必须精确匹配
            if ts_list[current_idx] == timestamp:
                if current_idx < lookback - 1:
                    return None
                else:
                    return data[current_idx - lookback + 1:current_idx + 1]
        else:
            # 跨周期数据:避免未来函数
            if ts_list[current_idx] == timestamp:
                if current_idx < lookback - 1:
                    return None
                else:
                    return data[current_idx - lookback + 1:current_idx + 1]  # 包含当前K线
            else:
                if current_idx < lookback:
                    return None
                else:
                    return data[current_idx - lookback:current_idx]  # 不包含当前K线

        return None
    def get_recent_data_bisect(self, symbol, timestamp, lookback=11, require_exact_match=True):
        """
        使用bisect库进行二分查找
        """
        if symbol in self.trade_data:
            data = self.trade_data[symbol]
        elif symbol in self.extra_data:
            data = self.extra_data[symbol]
        else:
            return None

        ts_list = data["timestamps"]
        if len(ts_list) == 0:
            return None

        # 找到最后一个 <= timestamp 的位置
        pos = bisect.bisect_right(ts_list, timestamp)
        if pos == 0:  # 所有数据都 > timestamp
            return None
        found_idx = pos - 1

        if require_exact_match:
            # 交易数据:必须精确匹配
            if ts_list[found_idx] != timestamp:
                return None
        
        # 统一的数据窗口检查
        if found_idx < lookback - 1:
            return None
            
        return data[found_idx - lookback + 1:found_idx + 1]
    @abstractmethod
    def on_bar(self, timestamp):
        """
        子类必须实现,K线回调函数 - 每个时间戳调用一次
        
        Args:
            timestamp: 当前时间戳
        """
        pass

    def calculate_signal(self, symbol, recent_data, timestamp):
        pass
